using Granite.Collections;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using Granite.Eventing;
using Granite.Commons.Tests.Mocks;
using Granite.Testing;

namespace Granite.Commons.Tests.Collections
{

	[TestClass()]
	public class ExtendedReadOnlyObservableCollectionTests
	{

		[TestMethod()]
		public void ExtendedReadOnlyObservableCollection_MemoryTest()
		{
			var source = new ExtendedObservableCollection<int>();
			var target = new ExtendedReadOnlyObservableCollection<int>(source);
			var wr = new WeakReference(target);

			var sourceEvents = new Queue<NotifyCollectionChangedEventArgs>();
			var targetEvents = new Queue<NotifyCollectionChangedEventArgs>();

			var sourceEvents2 = new Queue<PropertyChangedEventArgs>();
			var targetEvents2 = new Queue<PropertyChangedEventArgs>();

			source.CollectionChanged += (s, e) => sourceEvents.Enqueue(e);
			target.CollectionChanged += (s, e) => targetEvents.Enqueue(e);

			source.PropertyChanged += (s, e) => sourceEvents2.Enqueue(e);
			target.PropertyChanged += (s, e) => targetEvents2.Enqueue(e);

			GCCycle();

			source.Add(1);
			source.Add(2);
			source.Add(3);

			Assert.AreEqual(3, sourceEvents.Count);
			Assert.AreEqual(3, targetEvents.Count);
			Assert.AreEqual(6, sourceEvents2.Count);
			Assert.AreEqual(6, targetEvents2.Count);

			Assert.AreEqual(source[0], target[0]);
			Assert.AreEqual(source[1], target[1]);
			Assert.AreEqual(source[2], target[2]);

			target = null;

			GCCycle();

			Assert.IsFalse(wr.IsAlive);
		}

		[TestMethod]
		public void ExtendedReadOnlyObservableCollection_Constructor_Test1()
		{
			try
			{
				ExtendedObservableCollection<int> list = null;
				var result = new ExtendedReadOnlyObservableCollection<int>(list);
				Assert.Fail("Expected an ArgumentNullException");
			}
			catch (ArgumentNullException ex)
			{
				Assert.AreEqual("list", ex.ParamName, "Parameter name is wrong");
			}
		}

		[TestMethod]
		public void ExtendedReadOnlyObservableCollection_OnSourcePropertyChanged()
		{
			var list = new FooCollection();
			var result = new ReadOnlyFooCollection(list);
			var propertyAssert = new PropertyChangedEventAssert(result);

			list.Boom = 10;

			propertyAssert.Expect("Boom");
			Assert.AreEqual(10, result.Boom);
		}

		[TestMethod]
		public void ExtendedReadOnlyObservableCollection_OnSourcePropertyChanged2()
		{
			var list = new NotifierCollection();
			var result = new ExtendedReadOnlyObservableCollection<Notifier>(list);
			var propertyAssert = new PropertyChangedEventAssert(result);

			list.RaisePropertyName("");

			propertyAssert.Expect("");
		}

        [TestMethod]
        public void ExtendedReadOnlyObservableCollection_OnPropertyChanged2()
        {
            var list = new NotifierCollection();
            var result = new ReadOnlyNotifierCollection(list);
            var propertyAssert = new PropertyChangedEventAssert(result);

            result.RaisePropertyName("");

            propertyAssert.Expect("");
        }

		[TestMethod]
		public void ExtendedReadOnlyObservableCollection_OnItemPropertyChanged()
		{
			var list = new ExtendedObservableCollection<Notifier>();
			var result = new ExtendedReadOnlyObservableCollection<Notifier>(list);
			var itemPropertyAssert = new ItemPropertyChangedEventAssert(result);

			var notifier = new Notifier();
			list.Add(notifier);
			notifier.Name = "Frank";

			itemPropertyAssert.Expect(notifier, "Name");
		}

		/// <summary>
		/// This exercises the code path where we remove a listener without first adding one.
		/// </summary>
		[TestMethod]
		public void ExtendedReadOnlyObservableCollection_RemoveHandlerTest()
		{
			var list = new ExtendedObservableCollection<int>();
			var result = new ExtendedReadOnlyObservableCollection<int>(list);

			var collectionChangedEventQueue = new Queue<Tuple<object, NotifyCollectionChangedEventArgs>>();
			var collectionChangedListener = new Listener<NotifyCollectionChangedEventArgs>((s, e) => collectionChangedEventQueue.Enqueue(Tuple.Create(s, e)));
			result.RemoveHandler(collectionChangedListener);

			var propertyChangedEventQueue = new Queue<Tuple<object, PropertyChangedEventArgs>>();
			var propertyChangedListener = new Listener<PropertyChangedEventArgs>((s, e) => propertyChangedEventQueue.Enqueue(Tuple.Create(s, e)));
			result.RemoveHandler(propertyChangedListener);


			//TODO: Add this
			//var itemPropertyChangedEventQueue = new Queue<Tuple<object, RelayedEventArgs<PropertyChangedEventArgs>>>();
			//var itemPropertyChangedListener = new ItemPropertyChangedListener((s, e) => itemPropertyChangedEventQueue.Enqueue(Tuple.Create(s, e)));
			//result.RemoveHandler(itemPropertyChangedListener);
		}

		/// <summary>
		/// This exercises the code path where we remove a listener twice
		/// </summary>
		[TestMethod]
		public void ExtendedObservableDictionary_RemoveHandlerTest2()
		{
			var list = new ExtendedObservableCollection<int>();
			var result = new ExtendedReadOnlyObservableCollection<int>(list);

			var collectionChangedEventQueue = new Queue<Tuple<object, NotifyCollectionChangedEventArgs>>();
			var collectionChangedListener = new Listener<NotifyCollectionChangedEventArgs>((s, e) => collectionChangedEventQueue.Enqueue(Tuple.Create(s, e)));
			result.AddHandler(collectionChangedListener);
			result.RemoveHandler(collectionChangedListener);
			result.RemoveHandler(collectionChangedListener);

			var propertyChangedEventQueue = new Queue<Tuple<object, PropertyChangedEventArgs>>();
			var propertyChangedListener = new Listener<PropertyChangedEventArgs>((s, e) => propertyChangedEventQueue.Enqueue(Tuple.Create(s, e)));
			result.AddHandler(propertyChangedListener);
			result.RemoveHandler(propertyChangedListener);
			result.RemoveHandler(propertyChangedListener);

			//TODO: Add this
			//var itemPropertyChangedEventQueue = new Queue<Tuple<object, RelayedEventArgs<PropertyChangedEventArgs>>>();
			//var itemPropertyChangedListener = new ItemPropertyChangedListener((s, e) => itemPropertyChangedEventQueue.Enqueue(Tuple.Create(s, e)));
			//result.AddHandler(itemPropertyChangedListener);
			//result.RemoveHandler(itemPropertyChangedListener);
			//result.RemoveHandler(itemPropertyChangedListener);
		}





        [TestMethod]
        public void ExtendedObservableCollection_AddNullHandlerTest()
        {
            var list = new ExtendedObservableCollection<int>();
            var result = new ExtendedReadOnlyObservableCollection<int>(list);

            try
            {
				result.AddHandler((IListener<NotifyCollectionChangedEventArgs>)null);
                Assert.Fail("Expected an exception");
            }
            catch (ArgumentNullException ex)
            {
                Assert.AreEqual("eventHandler", ex.ParamName);
            }

            try
            {
				result.AddHandler((IListener<PropertyChangedEventArgs>)null);
                Assert.Fail("Expected an exception");
            }
            catch (ArgumentNullException ex)
            {
                Assert.AreEqual("eventHandler", ex.ParamName);
            }

            //try
            //{
            //    result.AddHandler((IItemPropertyChangedListener)null);
            //    Assert.Fail("Expected an exception");
            //}
            //catch (ArgumentNullException ex)
            //{
            //    Assert.AreEqual("eventHandler", ex.ParamName);
            //}
        }

        [TestMethod]
        public void ExtendedObservableCollection_RemoveNullHandlerTest()
        {
            var list = new ExtendedObservableCollection<int>();
            var result = new ExtendedReadOnlyObservableCollection<int>(list);

            try
            {
				result.RemoveHandler((IListener<NotifyCollectionChangedEventArgs>)null);
                Assert.Fail("Expected an exception");
            }
            catch (ArgumentNullException ex)
            {
                Assert.AreEqual("eventHandler", ex.ParamName);
            }

            try
            {
				result.RemoveHandler((IListener<PropertyChangedEventArgs>)null);
                Assert.Fail("Expected an exception");
            }
            catch (ArgumentNullException ex)
            {
                Assert.AreEqual("eventHandler", ex.ParamName);
            }

            //try
            //{
            //    result.RemoveHandler((IItemPropertyChangedListener)null);
            //    Assert.Fail("Expected an exception");
            //}
            //catch (ArgumentNullException ex)
            //{
            //    Assert.AreEqual("eventHandler", ex.ParamName);
            //}
        }






		static void GCCycle()
		{
			GC.Collect();
			GC.WaitForPendingFinalizers();
			GC.Collect();
		}

	}
}


